package org.swift.mashup.engine.jsr223;

import java.util.HashMap;
import java.util.Map;

import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptExecutor {

	static final String FACTORY_PARAMETER_THREADING = "THREADING";
	static final String THREADING_TYPE_MULTI 		= "MULTITHREADED";
	static final String THREADING_TYPE_ISOLATED 	= "THREAD-ISOLATED";
	static final String THREADING_TYPE_STATELESS 	= "STATELESS";
	
	private static final Map<String,Object> locks = new HashMap<String,Object>();
	
	synchronized private static Object getLock(String lockname) {
		Object lock = locks.get(lockname);
		if ( lock == null ) {
			lock = new Object();
			locks.put(lockname, lock);
		}
		return lock;
	}
	
	public Object execute(String language, String scriptcode, Map<String, ? extends Object> bindings)
		throws ScriptException {
	
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName(language);
		
		if ( engine == null ) {
			throw new ScriptException("No Script Engine found for language '"+language+"'");
		}

		Bindings finalbindings = engine.createBindings();
		finalbindings.putAll(bindings);
		
		if ( "rmi".equalsIgnoreCase(engine.getFactory().getEngineName()) ) {
			finalbindings.put(ScriptEngine.NAME, language);
		}
		
		String threadtype = (String)engine.getFactory().getParameter(FACTORY_PARAMETER_THREADING);
		Object returnobject = null;
		
		if ( THREADING_TYPE_MULTI.equalsIgnoreCase(threadtype)
			|| THREADING_TYPE_ISOLATED.equalsIgnoreCase(threadtype) 
			|| THREADING_TYPE_STATELESS.equalsIgnoreCase(threadtype) ) 
		{
			returnobject = evaluateScript(language, scriptcode, finalbindings, engine);
		}
		else {
			Object lock = getLock(language);
			synchronized(lock) {
				returnobject = evaluateScript(language, scriptcode, finalbindings, engine);
			}
		}
		
		return returnobject;
	}
	
	private Object evaluateScript(String language, String scriptcode, Bindings bindings, ScriptEngine engine)
		throws ScriptException {
	
		if ( ScriptCache.supportsCompilation(engine) ) {
			CompiledScript compiled = ScriptCache.getInstance().getCompiledScript(language, engine, scriptcode);
			return compiled.eval(bindings);
		}
		else {
			return engine.eval(scriptcode, bindings);
		}
	}
}
